//fl2440_timer.c
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/moduleparam.h>
#include <linux/errno.h>
#include <linux/cdev.h>

#include <asm/arch/regs-gpio.h>//GPIO口相关的宏定义 
#include <asm/hardware.h>//GPIO口操作相关的函数定义 

#include <asm/arch/regs-timer.h>//S3C2410_TCFG等的定义

#define DEVICE_NAME "fl2440_timer"


volatile unsigned long *gpbcon = NULL;
volatile unsigned long *gpbdat = NULL;

volatile unsigned long *tcfg0 = NULL;  //用来设置预分频
volatile unsigned long *tcfg1 = NULL;  //用来设置分频
volatile unsigned long *tcon  = NULL;  //定时器控制器
volatile unsigned long *tcntb0 = NULL; //计数缓冲寄存器
volatile unsigned long *tcmpb0 = NULL; //比较缓冲寄存器
volatile unsigned long *tcnto0 = NULL; //计数观察寄存器


volatile unsigned long *gpfcon = NULL;
volatile unsigned long *gpfdat = NULL;
volatile unsigned long *srcpnd = NULL;
volatile unsigned long *intmask = NULL;
volatile unsigned long *intpnd = NULL;
volatile unsigned long *intoffset = NULL;

static irqreturn_t irq_interrupt(int irq, int dev_id)
{
 //*gpbcon &=~(3<<0*2);
 //*gpbcon |= (2<<0*2);  //设置b0 为 tout0
 static unsigned int irq_count;
 irq_count++;
 printk("in irq_interrupt   %d\n",irq_count);

 if(irq_count%2)
  *gpbdat &= ~((1<<5) );
 else
  *gpbdat |= ((1<<5) );
 
 return IRQ_RETVAL(IRQ_HANDLED);
}

// ------------------- OPEN ------------------------
ssize_t drive_open (struct inode * inode ,struct file * file)
{
 
 *gpbcon &= ~((0x3<<(5*2)) | (0x3<<(6*2)) | (0x3<<(7*2)) | (0x3<<(8*2)));//清零
 *gpbcon |=   ((0x1<<(5*2)) | (0x1<<(6*2)) | (0x1<<(7*2)) | (0x1<<(8*2)));//设置成输出
 request_irq(IRQ_TIMER0, irq_interrupt, IRQ_TYPE_EDGE_FALLING, "TIME0", NULL);
 
 printk("-----------------drive open ok----------------\n");
 return 0;
}


// ------------------- RELEASE/CLOSE ---------------
ssize_t drive_release (struct inode  * inode ,struct file * file)
{
 free_irq(IRQ_TIMER0, NULL);
 printk("-----------------drive close ok----------------\n");
 return 0;
}


// ------------------- READ ------------------------
ssize_t drive_read (struct file * file ,char * buf, size_t count, loff_t * f_ops)
{
 printk("-----------------drive read ok----------------\n");
 
 //copy_to_user( buf,(const void *)press_cnt,sizeof(press_cnt) );
 
 return 0;
}


// ------------------- WRITE -----------------------
ssize_t drive_write (struct file * file ,const char * buf, size_t count, loff_t * f_ops)
{
 printk("-----------------drive write ok----------------\n");
 
 return 0;
}

// ------------------- IOCTL -----------------------
ssize_t drive_ioctl (struct inode * inode ,struct file * file, unsigned int cmd, unsigned long arg)
{
  struct clk *clk_p;

  unsigned int pclk;
  unsigned int tcnt;
  printk("-----------------drive ioctl ok----------------\n");

  //定时器时钟频率=pclk /( 预分频器的值+1) / 分频器
  //62.5k
  *tcfg0 &= ~S3C2410_TCFG_PRESCALER0_MASK;  //定时器0预分配清零
  *tcfg0 |=(50-1);  //预分频 50

  *tcfg1 &= ~S3C2410_TCFG1_MUX0_MASK;  //定时器0 mux 输入分频清零
  *tcfg1 |=S3C2410_TCFG1_MUX0_DIV16;   //mux 分频 16

  clk_p = clk_get(NULL, "pclk");
  pclk = clk_get_rate(clk_p);
  tcnt = (pclk/50/16)/10;  

  printk("pclk =  %d\n",pclk);
  printk("tcnt =  %d\n",tcnt);
  *tcntb0 &=0x0000; //16位寄存器
  *tcmpb0 &=0x0000;
  *tcntb0 |= 1000;
  *tcmpb0|=500;

  //*tcon &=0x0;
  // printk("tcon=%d\n",*tcon);
  printk("tcntb0=%d\n",*tcntb0);
  printk("tcmpb0=%d\n",*tcmpb0);

  *tcon &= ~0x1f; //清零
  // printk("tcon=%d\n",*tcon);
  msleep(10);
  //第一次必须手动更新
  *tcon |= 0xb;  //关闭死区、自动重载、关反相器、手动更新TCNTB0&TCMPB0、启动定时器0
  //1011
  //printk("tcon=%d\n",*tcon);
  msleep(10);
  *tcon &= ~0x02;  //清除定时器0的手动更新位
  //printk("tcon=%d\n",*tcon);
  
  printk("tcfg0=%d\n",*tcfg0);
  printk("tcfg1=%d\n",*tcfg1);
  printk("tcntb0=%d\n",*tcntb0);
  printk("tcmpb0=%d\n",*tcmpb0);
  printk("tcon=%d\n",*tcon);
  
  return 0;
}


// -------------------------------------------------

static struct file_operations drive_ops ={
 .owner  = THIS_MODULE,
 .open   = drive_open,
 .read    = drive_read,
 .write   = drive_write,
 .ioctl    = drive_ioctl,
 .release = drive_release,
};

static struct miscdevice misc = {
  .minor = MISC_DYNAMIC_MINOR,
  .name = DEVICE_NAME,
  .fops = &drive_ops,
};

static int __init init_drive(void)  
{
  int ret;
  ret = misc_register(&misc);

  gpbcon = (volatile unsigned long *)ioremap(0x56000010,32);
  gpbdat = (volatile unsigned long *)ioremap(0x56000014, 32);

  tcfg0    = (volatile unsigned long *)ioremap(0x51000000, 4);
  tcfg1    = (volatile unsigned long *)ioremap(0x51000004, 4);
  tcon     = (volatile unsigned long *)ioremap(0x51000008, 4);
  tcntb0  = (volatile unsigned long *)ioremap(0x5100000c, 4);
  tcmpb0 = (volatile unsigned long *)ioremap(0x51000010, 4);
  tcnto0  = (volatile unsigned long *)ioremap(0x51000014, 4);

  gpfcon = (volatile unsigned long *)ioremap(0x56000050, 4);
  gpfdat = (volatile unsigned long *)ioremap(0x56000054, 4);
  srcpnd = (volatile unsigned long *)ioremap(0x4a000000, 4);
  intmask= (volatile unsigned long *)ioremap(0x4a000008, 4);
  intpnd  = (volatile unsigned long *)ioremap(0x4a000010, 4);
  intoffset=(volatile unsigned long *)ioremap(0x4a000014, 4);
  printk("-----------------drive button init ok----------------\n");

  return 0;
}

static void __exit exit_drive(void)
{
  *tcon &=~1;
  misc_deregister(&misc);
  printk("-----------------drive button exit ok----------------\n");
}

module_init(init_drive);
module_exit(exit_drive);
MODULE_LICENSE("GPL");
